; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=3 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC

declare void @unknown() nocallback

define i32 @many_writes_nosycn(i1 %c0, i1 %c1, i1 %c2) nosync {
; CHECK: Function Attrs: norecurse nosync
; CHECK-LABEL: define {{[^@]+}}@many_writes_nosycn
; CHECK-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]], i1 [[C2:%.*]]) #[[ATTR1:[0-9]+]] {
; CHECK-NEXT:    [[P:%.*]] = alloca i32, align 4
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    br i1 [[C0]], label [[T0:%.*]], label [[F0:%.*]]
; CHECK:       t0:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    br i1 [[C1]], label [[T1:%.*]], label [[M1:%.*]]
; CHECK:       f0:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    br i1 [[C2]], label [[F1:%.*]], label [[M1]]
; CHECK:       t1:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    store i32 7, ptr [[P]], align 4
; CHECK-NEXT:    br label [[M2:%.*]]
; CHECK:       f1:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    store i32 9, ptr [[P]], align 4
; CHECK-NEXT:    br label [[M2]]
; CHECK:       m1:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    store i32 11, ptr [[P]], align 4
; CHECK-NEXT:    br label [[M2]]
; CHECK:       m2:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    [[L:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT:    ret i32 [[L]]
;
  %p = alloca i32
  store i32 0, ptr %p
  call void @unknown()
  store i32 1, ptr %p
  br i1 %c0, label %t0, label %f0
t0:
  store i32 2, ptr %p
  call void @unknown()
  store i32 3, ptr %p
  br i1 %c1, label %t1, label %m1
f0:
  store i32 4, ptr %p
  call void @unknown()
  store i32 5, ptr %p
  br i1 %c2, label %f1, label %m1
t1:
  store i32 6, ptr %p
  call void @unknown()
  store i32 7, ptr %p
  br label %m2
f1:
  store i32 8, ptr %p
  call void @unknown()
  store i32 9, ptr %p
  br label %m2
m1:
  store i32 10, ptr %p
  call void @unknown()
  store i32 11, ptr %p
  br label %m2
m2:
  call void @unknown()
  %l = load i32, ptr %p
  ret i32 %l
}

define i32 @many_writes(i1 %c0, i1 %c1, i1 %c2) {
; CHECK: Function Attrs: norecurse
; CHECK-LABEL: define {{[^@]+}}@many_writes
; CHECK-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]], i1 [[C2:%.*]]) #[[ATTR2:[0-9]+]] {
; CHECK-NEXT:    [[P:%.*]] = alloca i32, align 4
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    br i1 [[C0]], label [[T0:%.*]], label [[F0:%.*]]
; CHECK:       t0:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    br i1 [[C1]], label [[T1:%.*]], label [[M1:%.*]]
; CHECK:       f0:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    br i1 [[C2]], label [[F1:%.*]], label [[M1]]
; CHECK:       t1:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    store i32 7, ptr [[P]], align 4
; CHECK-NEXT:    br label [[M2:%.*]]
; CHECK:       f1:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    store i32 9, ptr [[P]], align 4
; CHECK-NEXT:    br label [[M2]]
; CHECK:       m1:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    store i32 11, ptr [[P]], align 4
; CHECK-NEXT:    br label [[M2]]
; CHECK:       m2:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    [[L:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT:    ret i32 [[L]]
;
  %p = alloca i32
  store i32 0, ptr %p
  call void @unknown()
  store i32 1, ptr %p
  br i1 %c0, label %t0, label %f0
t0:
  store i32 2, ptr %p
  call void @unknown()
  store i32 3, ptr %p
  br i1 %c1, label %t1, label %m1
f0:
  store i32 4, ptr %p
  call void @unknown()
  store i32 5, ptr %p
  br i1 %c2, label %f1, label %m1
t1:
  store i32 6, ptr %p
  call void @unknown()
  store i32 7, ptr %p
  br label %m2
f1:
  store i32 8, ptr %p
  call void @unknown()
  store i32 9, ptr %p
  br label %m2
m1:
  store i32 10, ptr %p
  call void @unknown()
  store i32 11, ptr %p
  br label %m2
m2:
  call void @unknown()
  %l = load i32, ptr %p
  ret i32 %l
}

declare void @usei32(i32) nocallback
; Ensure we use 42, not undef, for %l in the usei32 call and %r in the return.
define internal i32 @remote_write_and_read(ptr %p) norecurse {
; TUNIT: Function Attrs: norecurse
; TUNIT-LABEL: define {{[^@]+}}@remote_write_and_read
; TUNIT-SAME: (ptr noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR2]] {
; TUNIT-NEXT:    call void @usei32(i32 noundef 42)
; TUNIT-NEXT:    ret i32 undef
;
; CGSCC: Function Attrs: norecurse
; CGSCC-LABEL: define {{[^@]+}}@remote_write_and_read
; CGSCC-SAME: (ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR2]] {
; CGSCC-NEXT:    store i32 42, ptr [[P]], align 4
; CGSCC-NEXT:    [[L:%.*]] = load i32, ptr [[P]], align 4
; CGSCC-NEXT:    call void @usei32(i32 [[L]])
; CGSCC-NEXT:    ret i32 [[L]]
;
  store i32 42, ptr %p
  %l = load i32, ptr %p
  call void @usei32(i32 %l)
  ret i32 %l
}

define i32 @local_stack_remote_write_and_read() norecurse {
; TUNIT: Function Attrs: norecurse
; TUNIT-LABEL: define {{[^@]+}}@local_stack_remote_write_and_read
; TUNIT-SAME: () #[[ATTR2]] {
; TUNIT-NEXT:    [[A:%.*]] = alloca i32, align 4
; TUNIT-NEXT:    [[R:%.*]] = call i32 @remote_write_and_read(ptr noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[A]])
; TUNIT-NEXT:    ret i32 42
;
; CGSCC: Function Attrs: norecurse
; CGSCC-LABEL: define {{[^@]+}}@local_stack_remote_write_and_read
; CGSCC-SAME: () #[[ATTR2]] {
; CGSCC-NEXT:    [[A:%.*]] = alloca i32, align 4
; CGSCC-NEXT:    [[R:%.*]] = call i32 @remote_write_and_read(ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[A]])
; CGSCC-NEXT:    ret i32 [[R]]
;
  %a = alloca i32
  %r = call i32 @remote_write_and_read(ptr %a)
  ret i32 %r
}
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback }
; CHECK: attributes #[[ATTR1]] = { norecurse nosync }
; CHECK: attributes #[[ATTR2]] = { norecurse }
;.
